package com.cloudfun.campusshare.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.cloudfun.campusshare.common.constant.Code;
import com.cloudfun.campusshare.common.constant.ProductType;
import com.cloudfun.campusshare.common.exception.BusinessException;
import com.cloudfun.campusshare.common.model.vo.DeviceServiceTimeAddVO;
import com.cloudfun.campusshare.common.model.vo.DeviceServiceTimeListVO;
import com.cloudfun.campusshare.common.response.PageResult;
import com.cloudfun.campusshare.common.security.AuthenticationHolder;
import com.cloudfun.campusshare.entity.DeviceServiceTimeEntity;
import com.cloudfun.campusshare.entity.UserEntity;
import com.cloudfun.campusshare.entity.WashPointEntity;
import com.cloudfun.campusshare.repo.DeviceServiceTimeEntityRepo;
import com.cloudfun.campusshare.repo.WashPointConcretePositionEntityRepo;
import com.cloudfun.campusshare.repo.WashPointEntityRepo;
import com.cloudfun.campusshare.service.CreatorPersistenceService;
import com.cloudfun.campusshare.service.DeviceServiceTimeService;
import com.cloudfun.campusshare.service.UserRelService;
import com.cloudfun.campusshare.util.BeanCopyUtil;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import java.time.LocalTime;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author : zhangyongjie
 * @version : 1.0
 * @createTime : 2020/5/9 11:02
 * @description :
 */
@Service
public class DeviceServiceTimeServiceImpl extends CreatorPersistenceService implements DeviceServiceTimeService {
    @Resource
    private DeviceServiceTimeEntityRepo deviceServiceTimeEntityRepo;

    @Resource
    private WashPointEntityRepo washPointEntityRepo;

    @Resource
    private WashPointConcretePositionEntityRepo washPointConcretePositionEntityRepo;

    @Autowired
    private UserRelService userRelService;

    @Override
    protected Class<?> getEntityClass() {
        return DeviceServiceTimeEntity.class;
    }

    @Override
    public void addDeviceServiceTime(DeviceServiceTimeAddVO deviceServiceTimeAddVO) {
        if (StringUtils.isBlank(deviceServiceTimeAddVO.getId())) {
            WashPointEntity washPointEntity = washPointEntityRepo.findById(deviceServiceTimeAddVO.getWashPointId())
                    .orElseThrow(() -> new BusinessException(Code.ERROR, "找不到id:" + deviceServiceTimeAddVO.getWashPointId() + "的洗衣点"));
            DeviceServiceTimeEntity deviceServiceTimeEntity = BeanCopyUtil.copy(deviceServiceTimeAddVO, DeviceServiceTimeEntity.class);
            deviceServiceTimeEntity.setWashPointName(washPointEntity.getName());

            String currentId = AuthenticationHolder.getUserId();
            UserEntity rootUser = userRelService.getUserMerchant(currentId);
            deviceServiceTimeEntity.setMerchantId(rootUser.getId());
            deviceServiceTimeEntity.setBelongMerchantName(rootUser.getEnterpriseName());
            deviceServiceTimeEntity.setProductType(deviceServiceTimeAddVO.getProductType());
            deviceServiceTimeEntityRepo.save(deviceServiceTimeEntity);
        } else {
            DeviceServiceTimeEntity entity = deviceServiceTimeEntityRepo.findById(deviceServiceTimeAddVO.getId())
                    .orElseThrow(() -> new BusinessException(Code.ERROR, "找不到id:" + deviceServiceTimeAddVO.getId() + "服务时间记录"));
            entity.setWashPointId(deviceServiceTimeAddVO.getWashPointId());
            entity.setStartTime(deviceServiceTimeAddVO.getStartTime());
            entity.setEndTime(deviceServiceTimeAddVO.getEndTime());
            entity.setProductType(deviceServiceTimeAddVO.getProductType());
            deviceServiceTimeEntityRepo.save(entity);
        }
    }

    @Override
    public PageResult queryDeviceServiceTimeList(Pageable pageable, String keyword, LocalTime startTime, LocalTime endTime,
                                                 List<String> deviceType, String userId, List<String> washPointId, String productType) {
        Page<DeviceServiceTimeEntity> page = deviceServiceTimeEntityRepo
                .findAll(assembleDeviceServiceTimeSpecification(keyword, startTime, endTime, deviceType, userId, washPointId,  productType), pageable);
        return new PageResult(page.getTotalElements(), switchToVO(page.getContent()));
    }

    @Override
    public void deleteDeviceServiceTime(String deviceServiceTimeId) {
        if (AuthenticationHolder.isAdmin()) {
            //管理员直接删除
            deviceServiceTimeEntityRepo.deleteById(deviceServiceTimeId);
        } else {
            Boolean viewAllData = AuthenticationHolder.getUserAuth().getViewAllData();
            String userId = AuthenticationHolder.getUserId();
            if (viewAllData) {
                UserEntity merchant = userRelService.getUserMerchant(userId);
                if (Objects.isNull(deviceServiceTimeEntityRepo.findByIdAndMerchantId(deviceServiceTimeId, merchant.getId()))) {
                    throw new BusinessException(Code.ERROR, "您没有权限删除该数据");
                }
                deviceServiceTimeEntityRepo.deleteById(deviceServiceTimeId);
            } else {
                Set<String> allSub = userRelService.getDescendantIds(userId);
                allSub.add(userId);
                Optional.ofNullable(deviceServiceTimeEntityRepo.findByIdAndCreatorIdIn(deviceServiceTimeId, allSub))
                        .orElseThrow(() -> new BusinessException(Code.ERROR, "您没有权限删除该数据"));
                deviceServiceTimeEntityRepo.deleteById(deviceServiceTimeId);
            }
        }
    }

    @Override
    public List<DeviceServiceTimeListVO> queryTimeList4DeviceEdit(String deviceType, String washPointId) {
        return switchToVO(deviceServiceTimeEntityRepo.findAll(assembleDeviceServiceTimeSpecification(null, null,
                null, Lists.newArrayList(deviceType), null, Lists.newArrayList(washPointId),  null)));
    }

    private List<DeviceServiceTimeListVO> switchToVO(List<DeviceServiceTimeEntity> list) {
        return list.stream().map(x -> BeanCopyUtil.copy(x, DeviceServiceTimeListVO.class)).collect(Collectors.toList());
    }

    private Specification<DeviceServiceTimeEntity> assembleDeviceServiceTimeSpecification(String keyword, LocalTime startTime,
                                                                                          LocalTime endTime, List<String> deviceType,
                                                                                          String userId, List<String> washPointId,
                                                                                           String productType) {
        return ((root, query, criteriaBuilder) -> {
            List<Predicate> predicates = Lists.newArrayList();
            if (AuthenticationHolder.isAdmin()) {
                if (StringUtils.isNotBlank(userId)) {
                    predicates.add(criteriaBuilder.equal(root.get("merchantId"), userId));
                }
            } else {
                Boolean viewAllData = AuthenticationHolder.getUserAuth().getViewAllData();
                String currentId = AuthenticationHolder.getUserId();
                if (viewAllData) {
                    UserEntity merchant = userRelService.getUserMerchant(currentId);
                    predicates.add(criteriaBuilder.equal(root.get("merchantId"), merchant.getId()));
                } else {
                    Set<String> allSub = userRelService.getDescendantIds(currentId);
                    allSub.add(currentId);
                    predicates.add(root.get("creatorId").in(allSub));
                }
            }
            if (CollectionUtil.isNotEmpty(washPointId)) {
                predicates.add(root.get("washPointId").in(washPointId));
            }

            if (StringUtils.isNotBlank(keyword)) {
                List<Predicate> likes = Lists.newArrayList();
                likes.add(criteriaBuilder.like(root.get("belongMerchantName"), keyword));
                likes.add(criteriaBuilder.like(root.get("creatorName"), keyword));
                Predicate[] likeArr = new Predicate[likes.size()];
                predicates.add(criteriaBuilder.or(likes.toArray(likeArr)));
            }
            if (!Objects.isNull(startTime) && !Objects.isNull(endTime)) {
                predicates.add(criteriaBuilder.between(root.get("startTime"), startTime, endTime));
                predicates.add(criteriaBuilder.between(root.get("endTime"), startTime, endTime));
            }
            // TODO
            if (CollectionUtil.isNotEmpty(deviceType)) {
                predicates.add(root.get("productType").in(deviceType.stream().map(ProductType::valueOf).collect(Collectors.toList())));
            }
            if (StringUtils.isNotBlank(productType)) {
                CriteriaBuilder.In<Object> in = criteriaBuilder.in(root.get("productType"));
                String[] productTypes = productType.split(",");
                for (int i = 0; i < productTypes.length; i++) {
                    in.value(ProductType.valueOf(productTypes[i]));
                }
                predicates.add(in);
            }
            Predicate[] preArr = new Predicate[predicates.size()];
            return criteriaBuilder.and(predicates.toArray(preArr));
        });
    }
}
